using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Foreglance.MVP.Presenters;

namespace Foreglance.MVP.Views.Builders
{
    public class DetailViewBuilder : IDetailViewBuilder
    {
        private readonly List<Control> _controls = new List<Control>();

        public void Build<TEntity>(IEntityViewPresenter<TEntity> viewPresenter)
        {
            var container = ((Control)viewPresenter.View);
            foreach (var viewProperty in viewPresenter.ViewProperties)
            {
                var panel = new Panel {Dock = DockStyle.Top};
                container.Controls.Add(panel);
                if(viewProperty.Type == typeof(string))
                {
                    var control = new TextBox { Name = viewProperty.Name };
                    AddControl(control, panel);
                }
                else if (viewProperty.Type == typeof(int))
                {
                    var control = new NumericUpDown { Name = viewProperty.Name };
                    AddControl(control, panel);
                }
            }
            viewPresenter.OnEntitiesSelectionChanged += (sender, entities) => SetContext(container, viewPresenter, entities);
            viewPresenter.OnEntitiesEdited += (sender, entities) => UpdateContext(container, viewPresenter, entities);
            viewPresenter.OnEntitiesDeleted += (sender, entities) => ClearContext(container, viewPresenter, entities);
        }

        private void UpdateContext<TEntity>(Control container, IEntityViewPresenter<TEntity> viewPresenter, List<TEntity> entities)
        {
            if (!entities.Contains((TEntity)container.Tag))
                return;
            SetClearContext(container, default(TEntity), viewPresenter, true);
        }

        private void ClearContext<TEntity>(Control container, IEntityViewPresenter<TEntity> viewPresenter, List<TEntity> entities)
        {
            var entity = (TEntity)container.Tag;
            if (!entities.Contains(entity))
                return;
            SetClearContext(container, entity, viewPresenter, false);
        }

        private void AddControl(Control control, Panel panel)
        {
            panel.Controls.Add(control);
            panel.Height = control.Height + 6;
            _controls.Add(control);
        }

        private void SetContext<TEntity>(Control container, IEntityViewPresenter<TEntity> viewPresenter, IList<TEntity> entities)
        {
            if(entities.Count != 1)
                return;
            TEntity entity = entities[0];
            SetClearContext(container, entity, viewPresenter, false);
        }

        private void SetClearContext<TEntity>(Control container, TEntity entity, IEntityViewPresenter<TEntity> viewPresenter, bool clear)
        {
            var viewProperties = viewPresenter.ViewProperties;
            container.Tag = clear? (object) null: entity;
            var entityType = GetEntityType(entity);
            var entityProperties = entityType.GetProperties();
            foreach (var viewProperty in viewProperties)
            {
                var property = viewProperty;
                var entityProperty = entityProperties.FirstOrDefault(prop => prop.Name == property.Name);
                if (entityProperty == null)
                {
                    throw new Exception(string.Format("Property {0} not found in type {1}", viewProperty.Name,
                                                      entityType));
                }
                var control = _controls.FirstOrDefault(c => c.Name == viewProperty.Name);
                if(control == null)
                {
                    continue;
                }
                if (viewProperty.Type == typeof(string))
                {
                    (control).Text = clear ? string.Empty : (entityProperty.GetValue(entity, null) ?? string.Empty).ToString();
                }
                else if (viewProperty.Type == typeof(int))
                {
                    ((NumericUpDown)control).Value = clear ? 0m : Convert.ToDecimal((entityProperty.GetValue(entity, null) ?? 0));
                }
            }
        }

        private static Type GetEntityType<TEntity>(TEntity entity)
        {
            return typeof(TEntity);
        }
    }
}